///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNFileIO/inc/WNReadFileBuffer.h"
#include "WNMemory/inc/WNManipulation.h"
#include "WNMemory/inc/WNAllocation.h"
#include "WNContainers/inc/WNSerializer.h"

#include <stdarg.h>
#include <stdio.h>

using namespace WNFileIO;
using namespace WNMemory;

WNReadTextFileBuffer::WNReadTextFileBuffer(WNFileBufferType _type, WN_SIZE_T _buffSize):
    WNFileBuffer(_type),
    mBuffUsage(0),
    mBuffSize(_buffSize),
    mBuffPlace(0),
    mEndFile(WN_FALSE) {
    mCurrentBuff = static_cast<WN_CHAR*>(WNMalloc(_buffSize));

    WN_RELEASE_ASSERT(mCurrentBuff != WN_NULL);
}

WNReadTextFileBuffer::~WNReadTextFileBuffer() {
    if(mFile.IsValid()) {
        if(mCurrentBuff != WN_NULL) {
            WNFree(mCurrentBuff);
        }
    }
}

WN_CHAR* WNReadTextFileBuffer::ReserveBytes(WN_SIZE_T _numBytes, WN_SIZE_T& _returnedBytes) {   
    if(_numBytes + mBuffPlace > mBuffSize) {
        if(_numBytes > mBuffSize){
            WN_SIZE_T newBuffSize = _numBytes;
            mSpareBuff = static_cast<WN_CHAR*>(WNRealloc(mSpareBuff, newBuffSize));
            WN_DEBUG_ASSERT(mBuffPlace <= mBuffUsage);
            WNMemCpy(mSpareBuff, mCurrentBuff + mBuffPlace, mBuffUsage - mBuffPlace);
            WN_CHAR* temp = mCurrentBuff;
            mCurrentBuff = mSpareBuff;
            mSpareBuff = static_cast<WN_CHAR*>(WNRealloc(mCurrentBuff, newBuffSize));
            mBuffSize = newBuffSize;
            mBuffUsage -= mBuffPlace;
            mBuffPlace = 0;
        } else {
            WN_DEBUG_ASSERT(mBuffPlace <= mBuffUsage);
            WNMemCpy(mSpareBuff, mCurrentBuff + mBuffPlace, mBuffUsage - mBuffPlace);
            WN_CHAR* temp = mCurrentBuff;
            mCurrentBuff = mSpareBuff;
            mSpareBuff = temp;
            mBuffUsage -= mBuffPlace;
            mBuffPlace = 0;
        }
    }
    WN_SIZE_T haveData = mBuffUsage - mBuffPlace;
    if(!mEndFile && haveData < _numBytes){
        WN_SIZE_T read = mFile.ReadData(mCurrentBuff + mBuffUsage, _numBytes - haveData);
        if(read == 0){
            mEndFile = true;
        }
        mBuffUsage += read;
        haveData += read;
    }
    _returnedBytes = haveData > _numBytes? _numBytes : haveData;
    return(mCurrentBuff + mBuffPlace);
}

WNContainers::WNDataBufferType WNReadTextFileBuffer::GetType() {
    switch(mType){
        case WNFileBuffer::WNText:
            return(WNContainers::ReadText);
        case WNFileBuffer::WNBinary:
            return(WNContainers::ReadBinary);
    }
    return(WNContainers::ReadText);
}

WNFile::WNFileError WNReadTextFileBuffer::SetFile(const WN_CHAR* fileName) {
    mEndFile = WN_FALSE;
    WN_RELEASE_ASSERT_DESC(!mFile.IsValid(), "Error, trying to set a file on a text buffer that already exists");
    return(mFile.OpenFile(fileName, WNFile::WN_EXCLUSIVE | WNFile::WN_READ | WNFile::WN_STREAM));
}

WN_BOOL WNReadTextFileBuffer::Serialize(WN_UINT32 _flags, const WNContainers::WNSerializerBase& _serializer){
    mBuffPlace += _serializer.Serialize(*this, _flags);
    return(WN_TRUE);
}

